#!/usr/bin/python2.4
#
# Copyright 2009-2010 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========================================================================


# This main.scons file is the root description of the build specified in this
# directory and those under it. Individual components and the source files
# used to build them are described in subsidiary files called 'build.scons'.
#
# To build this project change the current directory to googleclient/Omaha.
# Then run hammer.bat
#
# A number of command line options can be specified. For a full list, use the
# -h and -H command options.
#
# Some useful options include:
#   -h   :   Prints a list of which modules and build variants are available
#            in this project.
#   -H   :   Print SCons specific help. SCons is a build tool which this
#            project uses in tandem with the Software Construction Toolkit
#            to build.
#   -c   :   Clean the project. I.e. delete all build output.
#   -j3  :   Run up to 3 build steps at once.
#
# Some useful build targets include:
#    all_programs    :   Build all programs generated by this project.
#    all_libraries   :   Build all libraries generated by this project.
#    all_tests       :   Build all tests generated by this project.
#    run_all_tests   :   Build and run all tests generated by this project.
#
# Some examples:
#   Build all programs and libraries in debug mode:
#     hammer
#   Build and run all tests in debug mode on windows:
#     hammer MODE=dbg-win run_all_tests
#   Build arbitrary library in the default mode:
#     hammer <library name>

import copy
import os
import sys

import omaha_version_utils

# To switch the build, simply change the value of the msc_ver variable below.
# VC2003/VC71 is 1310 but not supported by the current build.
# VC2005/VC80 is 1400.
# VC2008/VC90 is 1500.
_msc_ver = 1400

_sdk_path = os.environ['OMAHA_VISTASDK_DIR']

_signtool_path = os.path.join(_sdk_path, 'bin/signtool.exe')

# Build the "Google Update", the Google-specific version of Omaha.
# TODO(omaha): Set based on an environment variable and/or Hammer arg.
is_google_update_build = True

#
# Begin vendor-specific constants.
#
# These values must be changed when customizing open source Omaha.
# You may also wish to replace the language-neutral constants in the individual
# *generated_resources_*.rc files with localized strings.
_FULL_COMPANY_NAME = 'Google Inc.'
_SHORT_COMPANY_NAME = 'Google'
_PRODUCT_NAME = 'Update'
_COMPANY_DOMAIN_BASE = 'google'
_COMPANY_DOMAIN = _COMPANY_DOMAIN_BASE + '.com'
_MAIN_EXE_BASE_NAME = 'GoogleUpdate'
# TODO(omaha): Use this throughout the build files where goopdate and
# goopdateres are referenced.
_MAIN_DLL_BASE_NAME = 'goopdate'
# "Google Inc." must not be removed from the copyright string. This literal also
# appears as LegalCopyright in the VERSIONINFO section of .rc files.
# TODO(omaha): Use this variable in .wxs files, etc.
_OMAHA_COPYRIGHT_STRING_ENGLISH = 'Copyright 2007-2010 Google Inc.'


# TODO(omaha): Allow open source Omaha to be built without the Recovery MSI,
# which requires an extra certificate file and is unlikely to be used, or
# ClickOnce, which requires specifying a certificate hash. Disabling Recovery
# by default also means the open source build will work by default without
# having to check in SaveArguments.exe to SVN every 100 days.
# TODO(omaha3): Allow open source Omaha to be built without support for
# Omaha 2's COM APIs.

# The hash comes from the Thumbprint item in the Details tab of the file
# properties for the public key .cer file.
# TODO(omaha): Can we automate reading this and/or pass it on the command line?
_BUILD_SERVER_CERTIFICATE_HASH = 'cafd39335d6e76f0e26d81296e7cbbfbdf16a720'

#
# End vendor-specific constants.
#


# Windows is the only environment we bulid for, so create a specialized base
# environment for Windows.
win_env = Environment(
    # For internal builds only, it is a good idea to have set before the
    # 'component_setup' tool is used even though we add the SDK directories to
    # the appropriate environment variables manually below.
    PLATFORM_SDK_DIR = _sdk_path,
    tools=[
        'component_setup',
        'target_platform_windows',
        # Need to use 'masm' to override the 'as' tool currently used
        # by default in 'target_platform_windows'
        'masm',
        'atlmfc_vc80',
        'code_signing',
        'component_targets_msvs',
        'omaha_builders',
    ],
    msc_ver = _msc_ver,
    build_server_certificate_hash = _BUILD_SERVER_CERTIFICATE_HASH,
    # Visual Studio 2008 does not ship the sign tool. Use the sign tool from
    # the Platform SDK. This must come after the 'code_signing' tool is used.
    # Remove this if http://code.google.com/p/swtoolkit/issues/detail?id=16 is
    # fixed.
    SIGNTOOL = '"' + _signtool_path + '"',
)

# Remove this value because it conflicts with a #define
# in shlwapi.h in the Vista SDK
win_env.FilterOut(CPPDEFINES = ['OS_WINDOWS=OS_WINDOWS'])

# We pre-generate our own manifests, so make sure hammer does not generate
# default ones for us
del win_env['MANIFEST_FILE']

# Hack to work around bug in Hammer (http://b/1585388).
# TODO(Omaha): Remove when bug is fixed.
if win_env['ENV'].has_key('SYSTEMROOT'):
  if win_env['ENV'].has_key('SystemRoot'):
    del win_env['ENV']['SYSTEMROOT']
    del os.environ['SYSTEMROOT']

# Work around http://code.google.com/p/swtoolkit/issues/detail?id=10.
win_env['COMPONENT_TEST_SUBSYSTEM_WINDOWS'] = 1

# Declare command line options relating to code signing
# authenticode_file and authenticode_password are used by the normal signing
# tool and to sign manifests for ClickOnce.
# patching_certificate is used to create patchable MSI installers and MSPs.
# authenticode_file and authenticode_password are only used if !build_server.
# patching_certificate is used in all cases.
AddOption(
    '--authenticode_file',
    action='store',
    nargs=1,
    type='string',
    default='$MAIN_DIR/data/OmahaTestCert.pfx'
)

default_cert_password = 'test'
AddOption(
    '--authenticode_password',
    action='store',
    nargs=1,
    type='string',
    default=default_cert_password
)

AddOption(
    '--patching_certificate',
    action='store',
    nargs=1,
    type='string',
    default='$MAIN_DIR/data/OmahaTestCert.cer'
)

# Declare option for specifying path to new official build files
AddOption(
    '--official_build_path',
    action='store',
    nargs=1,
    type='string',
    default=None
)

# Declare option for specifying the set of official app installers to build.
# The value describes the name of the directory containing the official
# installer manifests and definitions.
AddOption(
    '--official_installer_app',
    action='store',
    nargs=1,
    type='string',
    default=None
)

AddOption(
    '--official_installer_file',
    action='store',
    nargs=1,
    type='string',
    default=None
)

AddOption(
    '--build_number',
    action='store',
    nargs=1,
    type='string',
    default=''
)

# Declare various boolean states.
DeclareBit('use_precompiled_headers', 'Use precompiled headers in build')
DeclareBit('official_installers', 'Building using checked-in binaries')
DeclareBit('build_server', 'Running on the build server')
DeclareBit('build_two_versions', 'Build second version for self-update testing')
DeclareBit('test_certificate', 'Files will be signed with the test certificate')
DeclareBit('bin', 'Building from pre-built binaries')
DeclareBit('min', 'Building minimal set of projects')
DeclareBit('all', 'Building all Projects')
DeclareBit('msvs', 'Building Visual Studio solution files')
DeclareBit('no-tests', 'Do not build the unit tests')

# Build official installers if --official_installers is on the command line.
win_env.SetBitFromOption('official_installers', False)

# Build as a build server if --build_server is on the command line.
win_env.SetBitFromOption('build_server', False)

# Build two versions if --build_two_versions is on the command line.
win_env.SetBitFromOption('build_two_versions', False)

# Store new versions of pre-built binaries if --bin is on the command line.
win_env.SetBitFromOption('bin', False)

# Build minimal set of libs if --min is on the command line.
win_env.SetBitFromOption('min', False)

# Build all libs if --all is on the command line.
win_env.SetBitFromOption('all', False)

# Build Visual Studio solution files if --msvs is on the command line.
win_env.SetBitFromOption('msvs', False)

# Do not build the unit tests if the bit is set.
win_env.SetBitFromOption('no-tests', False)

# Build all directories and two versions if this is the build server.
if win_env.Bit('build_server'):
  win_env.SetBits('all')
  win_env.SetBits('build_two_versions')

# Make sure 'all' overrides 'min'.
if win_env.Bit('all'):
  win_env.ClearBits('min')

# Allow use of command-line-specified certificates to sign with, but
# only if we're not on the build server.
if not win_env.Bit('build_server'):
  win_env.Replace(
    CERTIFICATE_PATH=GetOption('authenticode_file'),
    CERTIFICATE_PASSWORD=GetOption('authenticode_password'),
  )

  # Store whether we're using the default test cert separately, because
  # we won't always know what the default password was.
  if GetOption('authenticode_password') is default_cert_password:
    win_env.SetBits('test_certificate')


# The precompiled headers are to be used as an optional build speed up
# build facility. Individual compilation units in the project must build with
# or without precompiled headers. Building without precompiled headers is sort
# of meaningless, since all the time we should build with it. However,
# eliminating the dependency is desirable from a few reasons:
# 1. making sure the files in the project include all the definitions they need
# 2. creating different precompile headers if needed.
# 3. making sure the precompile headers do not add to the size bloat.
# There are two current limitations with the current setup.
# First, due to pushing the warning level to W4 and WAll, we rely on the
# common precompile.h to properly turn off the warnings inside system and
# library code.
# Second, to override the ATLASSERT, a file must be included before any of
# the atl headers. To do this on a case by case basis is impractical and
# error prone.
# Therefore, when building with precompile headers off, the code is
# building on W3 and it is not taking over the ATL asserts.
win_env.SetBitFromOption('use_precompiled_headers', True)

if win_env.Bit('use_precompiled_headers'):
  print 'Using precompiled headers.'


#
# Set up version info.
#
omaha_version_info = omaha_version_utils.OmahaVersionInfo('VERSION')
omaha_versions_info = [omaha_version_info]

if win_env.Bit('build_two_versions'):
  omaha_test_version_info = copy.deepcopy(omaha_version_info)
  omaha_test_version_info.MakeTestVersion()
  omaha_versions_info.append(omaha_test_version_info)

# Print the version(s) being built.
print 'Building versions: %s' % ', '.join(
    [version_info.GetVersionString() for version_info in omaha_versions_info])

build_number = GetOption('build_number')
if build_number:
  print 'Build number: %s' % build_number

win_env['omaha_versions_info'] = omaha_versions_info

if is_google_update_build:
    win_env.Append(
        CPPDEFINES = ['GOOGLE_UPDATE_BUILD'],
        RCFLAGS = ['/DGOOGLE_UPDATE_BUILD=1'],
    )

# Make sure python.exe can be located.
win_env.AppendENVPath('PATH', os.environ['OMAHA_PYTHON_DIR'])

win_env.Append(
    # Add windows specific compiler flags.
    CCFLAGS = [
        '/nologo',
        '/c',
        '/Zc:forScope',
        '/D_HAS_EXCEPTIONS=0',
        '/DCOMPILER_MSVC',
        '/J',
        '/DSTL_MSVC',
        '/GR-',
        '/D_CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES=1',
        '/D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1',
        '/D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1',
        '/WX',      # warnings as errors

        #
        # Disable the following level 4 warnings below.
        #
        '/wd4127',  # conditional expression is constant
        '/wd4189',  # local variable is initialized but not referenced
        '/wd4505',  # unreferenced local function has been removed

        #
        # Disable the pedantic warnings below.
        #
        '/wd4191',  # unsafe conversion from 'type of expression' to
                    #     'type required'
        '/wd4217',  # member template functions cannot be used for
                    #     copy-assignment...
        '/wd4365',  # conversion from 'type_1' to 'type_2',
                    #     signed/unsigned mismatch
        '/wd4512',  # assignment operator could not be generated
        '/wd4514',  # unreferenced inline function has been removed
        '/wd4555',  # expression has no effect
        '/wd4619',  # #pragma warning : there is no warning number 'number'
        '/wd4623',  # default constructor could not be generated...
        '/wd4625',  # copy constructor could not be generated...
        '/wd4626',  # assignment operator could not be generated...
        '/wd4668',  # not defined as a preprocessor macro, replacing with '0'.
        '/wd4710',  # function not inlined
        '/wd4711',  # function 'function' selected for inline expansion
        '/wd4738',  # storing 32-bit float result in memory...
        '/wd4820',  # bytes padding added after construct 'member_name'
        ],

    # Where to look for include files.
    CPPPATH = [
        '$MAIN_DIR',
        '$MAIN_DIR/..',
        '$MAIN_DIR/third_party/chrome',
        '$MAIN_DIR/third_party/gtest/include',
        ],

    # Defines for windows environment.
    CPPDEFINES = [
        'WIN32', '_WINDOWS',
        'UNICODE', '_UNICODE',
        'WIN32_LEAN_AND_MEAN',
        'STRICT',
        'SECURITY_WIN32',
        '_ATL_ALL_WARNINGS',
        '_ATL_CSTRING_EXPLICIT_CONSTRUCTORS',
        '_ATL_CSTRING_NO_CRT',
        '_ATL_NO_ACLAPI',
        '_ATL_NO_DEFAULT_LIBS',
        '_ATL_NO_EXCEPTIONS',
        '_ATL_NO_GLOBAL_SOCKET_STARTUP',
        '_ATL_NO_PERF_SUPPORT',
        '_ATL_NO_TRACK_HEAP',
        '_ATL_NO_UUIDOF',
        '_ATL_STATIC_REGISTRY',
        '_CRT_RAND_S',      # rand_s support available in Windows XP only.
        '_WTL_NO_CSTRING',  # WTL uses ATL CString instead.

        # '_ATL_NO_CONNECTION_POINTS',
        # '_ATL_NO_DOCHOSTUIHANDLER',
        # '_ATL_NO_HOSTING',

        # Set these C_DEFINES when APPVER=5.0
        # (see win32.mak in Platform SDK)
        # Target Windows XP and IE 6.0 and above.
        'WINVER=0x0501',
        '_WIN32_WINNT=0x0501',
        '_WIN32_IE=0x0600',
        '_RICHEDIT_VER=0x0010',

        # don't define min and max in windef.h
        'NOMINMAX',

        # Logging is enabled in all modes for diagnostics purposes.
        'LOGGING',

        'FULL_COMPANY_NAME_ANSI=\\"%s\\"' % _FULL_COMPANY_NAME,
        'SHORT_COMPANY_NAME_ANSI=\\"%s\\"' % _SHORT_COMPANY_NAME,
        'PRODUCT_NAME_ANSI=\\"%s\\"' % _PRODUCT_NAME,
        'COMPANY_DOMAIN_BASE_ANSI=\\"%s\\"' % _COMPANY_DOMAIN_BASE,
        'COMPANY_DOMAIN_ANSI=\\"%s\\"' % _COMPANY_DOMAIN,
        'OMAHA_APP_NAME_ANSI=\\"%s %s\\"' % (
            _SHORT_COMPANY_NAME, _PRODUCT_NAME),
        'MAIN_EXE_BASE_NAME_ANSI=\\"%s\\"' % _MAIN_EXE_BASE_NAME,
        'MAIN_DLL_BASE_NAME_ANSI=\\"%s\\"' % _MAIN_DLL_BASE_NAME,
        'OFFICIAL_BUILD=%d' % win_env.Bit('build_server'),
        'TEST_CERTIFICATE=%d' % win_env.Bit('test_certificate'),
        'ONECLICK_PLUGIN_NAME=_T(\\"%s\\")' % (
            omaha_version_utils.GetONECLICK_PLUGIN_NAME()),
        'ONECLICK_PLUGIN_VERSION_ANSI=\\"%d\\"' % (
            omaha_version_info.oneclick_plugin_version),
        'ONECLICK_PLUGIN_FILENAME=_T(\\"%s\\")' % (
            omaha_version_info.oneclick_plugin_filename),
        'UPDATE_PLUGIN_NAME=_T(\\"%s\\")' % (
            omaha_version_utils.GetUPDATE_PLUGIN_NAME()),
        'UPDATE_PLUGIN_VERSION_ANSI=\\"%d\\"' % (
            omaha_version_info.update_plugin_version),
        'UPDATE_PLUGIN_FILENAME=_T(\\"%s\\")' % (
            omaha_version_info.update_plugin_filename),
        'BHO_NAME=_T(\\"%s\\")' % omaha_version_utils.GetBHO_NAME(),
        'BHO_FILENAME=_T(\\"%s\\")' % omaha_version_info.bho_filename,
        'CRASH_HANDLER_NAME=_T(\\"%s\\")' % omaha_version_utils.GetCRASH_HANDLER_NAME(),
        ],

    # Link in some windows libraries.
    LIBS = [
        'advapi32',
        'comdlg32',
        'gdi32',
        'kernel32',
        'odbc32',
        'odbccp32',
        'ole32',
        'oleaut32',
        'shell32',
        'user32',
        'uuid',
        'winspool',
        ],

    # Common linker flags.
    LINKFLAGS = [
        '/nologo',
        '/SUBSYSTEM:WINDOWS',
        '/MACHINE:X86',
        '/RELEASE',
        '/MAP',
        '/NODEFAULTLIB',
        '/DYNAMICBASE',   # Enable ASLR. See http://goo.gl/k2IE.
        '/NXCOMPAT',      # Enable NX support. See http://goo.gl/k2IE.
        '/SAFESEH',
        ],

    # Shared library specific linker flags.
    SHLINKFLAGS = [
        '/nologo',
        '/SUBSYSTEM:WINDOWS',
        '/MACHINE:x86',
        ],

    # Resource compiler flags.
    # Defines in CCFLAGS are automatically included.
    RCFLAGS = [
        '/l 1033',  # /l == default language ID
        '/DBUILD_NUMBER=\\"%s\\"' % build_number,
        '/DOMAHA_COPYRIGHT_STRING_ENGLISH=\\"%s\\"' % (
            _OMAHA_COPYRIGHT_STRING_ENGLISH),
        ],
)

# Allow verification of these settings in the build log.
if win_env.Bit('build_server'):
  print 'OFFICIAL_BUILD=1'
  print 'TEST_CERTIFICATE=%d' % win_env.Bit('test_certificate')


# Add the parent directory of the main omaha directory to the Python path so
# that we can import using the format "omaha.subdir.module".
sys.path.append(os.path.split(win_env.Dir('$MAIN_DIR').abspath)[0])

# Make sure Vista SDK in all the important paths earlier in path than VC80.
for mid_dir in ['', 'vc']:
  for env_var, sub_dir in [('PATH', 'bin'),
                           ('INCLUDE', 'include'),
                           ('LIB', 'lib')]:
    var_path = os.path.join(_sdk_path, mid_dir, sub_dir)
    if os.path.exists(var_path):
      win_env.PrependENVPath(env_var, var_path)

if not win_env.Bit('official_installers'):
  win_env.Append(CPPPATH = os.environ['OMAHA_WTL_DIR'])

  # Make sure csc.exe can be located.
  win_env.AppendENVPath('PATH', os.environ['OMAHA_NET_DIR'])

  sys.path.append('tools')  # for import proxy_clsid_utils.py
  import proxy_clsid_utils

  # Generate uniqe proxy CLSIDs for each build.
  win_env.Execute('python $MAIN_DIR\\tools\\proxy_clsid_utils.py')
  win_env.Append(
      CPPDEFINES = [
        'PROXY_CLSID_IS_MACHINE=%s' % proxy_clsid_utils.GetMachineProxyClsid(),
        'PROXY_CLSID_IS_USER=%s' % proxy_clsid_utils.GetUserProxyClsid(),
      ],
  )

# WiX path has to be added before the WiX tool can be called.
win_env.AppendENVPath('PATH', os.environ['OMAHA_WIX_DIR'])

win_env.Tool('wix')


_base_dirs = [
    '.',
    'base',
    'clickonce',
    'client',
    'common',
    'core',
    'google_update',
    'goopdate',
    'net',
    'service',
    'setup',
    'statsreport',
    'third_party',
    'tools',
    'ui',
    ]

_normal_dirs = [
    'google_update_b4451148',
    'installers',
    'mi_exe_stub',
    'plugins',
    'recovery',
    ]

_official_installers_dirs = [
    'installers',
    ]

_extra_dirs = [
    'enterprise',
    'standalone',
    ]

#
# Need to decide which subdirs need to be built.
#
_dirs_to_build_set = set()

if win_env.Bit('official_installers'):
  # Only want to build very specific subdirs.
  win_env.SetBits('no-tests')
  _dirs_to_build_set.update(_official_installers_dirs)
elif not win_env.Bit('bin'):
  # All other configs get the base dirs.
  _dirs_to_build_set.update(_base_dirs)

  if win_env.Bit('min'):
    print '*** Building Minimal Set of Projects ***'
  else:
    _dirs_to_build_set.update(_normal_dirs)

  if win_env.Bit('all'):
    _dirs_to_build_set.update(_extra_dirs)

# Build Google application-specific metainstallers.
if os.path.exists(win_env.Dir('$MAIN_DIR/internal').abspath):
  _dirs_to_build_set.update(['internal'])

_dirs_to_build = list(_dirs_to_build_set)

# This must be the last directory.
if not win_env.Bit('no-tests'):
  _dirs_to_build.append('testing')

# Instruct Hammer which dirs to build.
win_env['BUILD_SCONSCRIPTS'] = _dirs_to_build

# These are used by the Omaha Builder OmahaUnittest(). They must be added to the
# environment because there must be one per-mode.
win_env['all_in_one_unittest_sources'] = []
win_env['all_in_one_unittest_libs'] = set()


# Create the leaf debug Windows environment.
windows_debug_env = win_env.Clone(
    # Give this build a name and a description.
    BUILD_TYPE = 'dbg-win',
    BUILD_TYPE_DESCRIPTION = 'Windows debug build',
)

# Use common debug settings.
windows_debug_env.Tool('target_debug')

windows_debug_env.Append(
    CCFLAGS = [
        '/RTC1',
        '/Od',
        '/MTd',
        ],
    CPPDEFINES = [
        '_DEBUG',
        'DEBUG',
        ],
)


# Create the leaf optimized Windows environment.
windows_optimized_env = win_env.Clone(
    # Give this build a name and a description.
    BUILD_TYPE = 'opt-win',
    BUILD_TYPE_DESCRIPTION = 'Windows optimized build',
)

# Use common optimized settings.
windows_optimized_env.Tool('target_optimized')

windows_optimized_env.Append(
    CCFLAGS = [
        '/O1',        # Optimize for small size.
        '/GS',
        '/FD',
        '/GL',        # Global optimization goes with link flag '/LTCG'
        '/MT',
        ],
    CPPDEFINES = [
        'NDEBUG',
        'SHIPPING'    # code in 'common' needs this
        ],
    ARFLAGS = [
        '/LTCG',      # Set LTCG for creation of .lib files too.
        ],
    LINKFLAGS = [
        '/incremental:no',
        '/opt:ref',
        '/opt:icf=32',
        '/opt:nowin98',
        '/LTCG',      # Link-time code generation goes with cl flag '/GL'
        ],
)


# Create an environment for coverage test builds, based on the dbg build.
windows_coverage_env = windows_debug_env.Clone(
    BUILD_TYPE = 'coverage-win',
    BUILD_TYPE_DESCRIPTION = 'Windows coverage build',
)
# The Coverage build require additional tools that not everyone has. Therefore,
# it should build as part of the all group.
windows_coverage_env.FilterOut(BUILD_GROUPS=['all'])

windows_coverage_env.Tool('code_coverage')

# Coverage will run omaha_unittest.exe, which requires some extra environment.
for env_var in os.environ:
  if not env_var in windows_coverage_env['ENV']:
    windows_coverage_env['ENV'][env_var] = os.environ[env_var]

# Create a target that covers everything in the staging dir, as many of those
# files will be required for the unittests to run successfully.
# TODO(omaha3): This may not be necessary when using ComponentTestProgram. If it
# is, it needs to be changed to use test/ instead of $STAGING_DIR/.
windows_coverage_env.Alias(
    'run_omaha_unittest_for_coverage',
    '$STAGING_DIR',
    '$STAGING_DIR/omaha_unittest.exe'
)
windows_coverage_env.Append(
    # TODO(omaha): We cannot run our unit tests on the new build system. Ensure
    # coverage works with the new test execution system.
    # COVERAGE_TARGETS=['run_omaha_unittest_for_coverage'],
    COVERAGE_INSTRUMENTATION_PATHS=['$STAGING_DIR'],
    # This value should only be used in code if absolutely necessary.
    CPPDEFINES=['COVERAGE_ENABLED'],
)

# TODO(omaha): Prevent the analyzer, which will fail, from running until we can
# run unit tests on the build system. See the TODO above.
windows_coverage_env['COVERAGE_START_CMD'] = '@echo Not starting coverage'
windows_coverage_env['COVERAGE_STOP_CMD'] = '@echo Not ending coverage'

# Skip signing in coverage build until the last step.
windows_coverage_env['SIGNTOOL_ORIG'] = windows_coverage_env['SIGNTOOL']
windows_coverage_env['SIGNTOOL'] = '@echo Signing deferred: '

def SigningCommand(env, filename):
  # Only do signing if there is a certificate file or certificate name.
  if env.subst('$CERTIFICATE_PATH') or env.subst('$CERTIFICATE_NAME'):
    # The command used to do signing (target added on below).
    signing_cmd = '$SIGNTOOL_ORIG sign '
    # Add in certificate file if any.
    if env.subst('$CERTIFICATE_PATH'):
      signing_cmd += ' /f "$CERTIFICATE_PATH"'
      # Add certificate password if any.
      if env.subst('$CERTIFICATE_PASSWORD'):
        signing_cmd += ' /p "$CERTIFICATE_PASSWORD"'
    # Add certificate store if any.
    if env.subst('$CERTIFICATE_NAME'):
      # The command used to do signing (target added on below).
      signing_cmd += ' /s "$CERTIFICATE_STORE" /n "$CERTIFICATE_NAME"'
    # Add timestamp server if any.
    if env.subst('$TIMESTAMP_SERVER'):
      signing_cmd += ' /t "$TIMESTAMP_SERVER"'
    # Add in target name
    signing_cmd += ' "%s"' % filename
    return signing_cmd
  else:
    return 'echo no signing needed'

def _IsInstrumentableFileType(file):
  if (file.endswith('.exe') or
      file.endswith('.dll')):
    return True
  return False

def _IsSignableFileType(file):
  if (file.endswith('.exe') or
      file.endswith('.dll') or
      file.endswith('.msi') or
      file.endswith('.msp')):
    return True
  return False

# Sign files during the install stage, since instrumentation invalidates the
# signature. Signing within the individual build files was disabled above.
# Do not sign intermediate "_unsigned" or "_unittest" files.
# Instrumented files must be signed after installing becuase install is what
# does the instrumentation. This also seems to be required to avoid
# unnecessarily rebuilding non-instrumentable files.
def _PostCoverageSigningInstall(dest, source, env):
  if _IsInstrumentableFileType(dest) or not _IsSignableFileType(dest):
    # Install the file to staging. Includes instrumentation if appropriate.
    env['PRECOVERAGE_SIGN_INSTALL'](dest, source, env)
  else:
    # For signable but not instrumentable files, copy the files rather than
    # using PRECOVERAGE_SIGN_INSTALL as this works around unnecessary rebuilds
    # caused by http://code.google.com/p/swtoolkit/issues/detail?id=13.
    env.Execute('copy "%s" "%s"' % (source, dest))

  if (_IsSignableFileType(dest) and
      (-1 == dest.find('_unsigned')) and
      (-1 == dest.find('_unittest')) and
      os.path.split(os.path.split(dest)[0])[1] == 'staging'):
    env.Execute(SigningCommand(env, dest))

windows_coverage_env['PRECOVERAGE_SIGN_INSTALL'] = (
    windows_coverage_env['INSTALL'])
windows_coverage_env['INSTALL'] = _PostCoverageSigningInstall


# Make debug the default build after any copies of it have been made.
windows_debug_env.Append(BUILD_GROUPS = ['default'])

# ----------------------------------------------------------

# Build the variants listed above.
# This step will call each of the SConscripts (build.scons) listed,
# once for each variant currently being built.
BuildEnvironments(
    [ windows_debug_env,
      windows_optimized_env,
      windows_coverage_env,
    ]
)

if 'HAMMER_RUNS_TESTS' in os.environ.keys():
  # Hammer sets the default target to 'scons-out'. This causes run_* aliases
  # to also be built, which means the tests run by default. To avoid this, clear
  # Default and set the default to just build the programs.
  Default(None)
  # TODO(omaha): Not all of our core binaries are included in these three
  # aliases. This is because SignedBinary() and Command() do not add the outputs
  # to a group. Fix this.
  Default(['all_programs', 'all_libraries', 'all_test_programs'])

if win_env.Bit('msvs'):
  source_project = win_env.ComponentVSDirProject('all_source', ['$MAIN_DIR'])

  # 'all_*' values do not appear to be populated until after BuildEnvironments
  # is called. Thus, the solution will be specific to either debug or optimized.
  # ComponentVSSourceProject() might be more desirable, but it does not appear
  # to work.
  windows_debug_env.ComponentVSSolution('omaha_dbg',
                                        ['all_programs', 'all_libraries'],
                                        projects=[source_project])
